﻿using System;
using Oblivion.Clients;
using Oblivion.Networking.Packets;

namespace Oblivion.Networking
{
    public class PacketProcessor
    {
        public static void ProcessPacket(byte[] data, GameClient client)
        {
            var packet = new Packet(data);
            var type = (PacketType) packet.ReadUInt8();
            packet.FinishReading();

            Program.WriteLine("Received packet: " + type.ToString() + " (0x" + data[0].ToString("X") + ")",
                              ConsoleColor.Magenta);

            switch (type)
            {
                    #region ServerInfo (0xFE)

                case PacketType.ServerInfo:
                    {
                        if (data.Length > 0)
                        {
                            if (data[1] == 1) //Good to go
                            {
                                client.Send(new KickPacket {Reason = new ServerInfoPacket().info()}.Build());
                            }
                        }
                        break;
                    }

                    #endregion

                    #region Handshake (0x02)

                case PacketType.Handshake:
                    {
                        var pkt = new HandshakePacket(data);
                        client.Username = pkt.Username;
                        Program.WriteLine("Username: " + client.Username + " Protocol Version: " + pkt.ProtocolVersion);

                        #region Sanity Checks

                        if (pkt.ProtocolVersion != Program.Configuration.PROTOCOL_VERSION)
                        {
                            client.Send(
                                new KickPacket
                                    {
                                        Reason =
                                            "Please update your client to version: " +
                                            Program.Configuration.TARGET_CLIENT_VERSION
                                    }.Build());
                        }
                        if (pkt.ProtocolVersion > Program.Configuration.PROTOCOL_VERSION)
                        {
                            client.Send(
                                new KickPacket
                                    {
                                        Reason =
                                            "Server is outdated! Please downgrade to version: " +
                                            Program.Configuration.TARGET_CLIENT_VERSION
                                    }.Build());
                        }
#warning TODO: Check if the client is already logged in - Gotta leave noc something todo <3

                        #endregion

                        //Encryption stuffz! :D :S?
                        client.Hash = EncryptionRequestPacket.CreateHash();
                        client.Send(new EncryptionRequestPacket(client).Build());
                        Program.WriteLine("Encryption request packet sent.");
                        break;
                    }

                    #endregion

                    #region EncryptionResponse (0xFC)

                case PacketType.EncryptionResponse:
                    {
                        var pkt = new EncryptionResponsePacket(data);
                        client.SharedKey = Program.CryptoServiceProvider.Decrypt(pkt.SharedSecret, false);
                        client.Send(new EncryptionResponsePacket().Build());

                        client.IsEncryptioned = true;
                        client.SetupEncryption();

                        Program.WriteLine("Client encryption enabled!", ConsoleColor.Cyan);
                        break;
                    }

                    #endregion

                    #region Payload (0xCD)

                case PacketType.Payload:
                    {
                        var pkt = new PayloadPacket(data);
                        switch (pkt.Type)
                        {
                            case PayloadPacket.PayloadType.InitialSpawn:
                                {
                                    if (!client.LoggedIn)
                                    {
                                        Program.WriteLine(client.Username + " First login - Loading data");

                                        client.LoggedIn = true;
                                    }
                                    break;
                                }
                            case PayloadPacket.PayloadType.Respawn:
                                {
                                    if (!client.LoggedIn)
                                    {
                                        Program.WriteLine(client.Username +
                                                          " Is trying to respawn without logging in, disconnecting");
                                        client.Kick("Client not logged in - Expected InitialSpawn not Respawn");
                                        return;
                                    }
                                    break;
                                }
                            default:
                                Program.WriteLine("Payload packet, Received unknown type: " + pkt.Type);
                                break;
                        }
                        break;
                    }

                    #endregion
            }
        }
    }
}